<?php
declare (strict_types = 1);

namespace app\service\logic\common\order;

use app\exception\LogicException;
use app\facade\ALiPayFacade;
use app\facade\StringFacade;
use app\facade\WechatpayFacade;
use app\model\communal\CourseModel;
use app\model\communal\OrderCourseModel;
use app\model\communal\OrderMultiModel;
use app\model\communal\OrderMultiSubModel;
use app\model\communal\RolePermissionsModel;
use app\model\communal\ScaleMultiExpertModel;
use app\model\communal\ScaleMultiModel;
use app\model\communal\ScaleMultiTopicModel;
use app\model\communal\ScaleMultiTypeModel;
use app\service\logic\BaseService;
use app\service\logic\common\course\CourseService;
use app\service\logic\common\scale\MultiExpertService;
use app\service\logic\common\scale\MultiService;
use app\service\logic\common\user\ExpertService;
use app\service\logic\common\user\StatisticsService;
use Carbon\Carbon;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\facade\Db;
use think\facade\Log;
use think\Model;

final class OrderCourseService extends BaseService
{
    protected string $modelClassName = 'OrderCourseModel';

    /**
     * @param $params
     * @param array $search_fields
     * @param array $search
     * @param array $with
     * @param null $eachCall
     * @param bool $isPage
     * @return array
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function getAppList($params, array $search_fields = [], array $search = [], $with = [], $eachCall = null, $isPage = true)
    {
        list($page, $page_size) = $this->pageByParams($params);
        $order = $this->orderByParams($params);

        $query = OrderCourseModel::field(['id', 'course_name', 'course_image', 'total_amount', 'expired_at'])
            ->with($with)
            ->withSearch($search_fields, $search)
            ->order($order);
        if ($isPage) {
            $query = $query->page($page, $page_size);
        }
        $query = $query->select();
        if (is_callable($eachCall)) {
            $query = $query->each($eachCall);
        }
        $list = $query->toArray();
        $total = OrderCourseModel::withSearch($search_fields, $search)->count();

        return [
            'items' => $list,
            'total' => $total,
        ];
    }

    /**
     * @param $params
     * @param array $with
     * @param null $eachCall
     * @return OrderCourseModel|array|false|mixed|Model
     * @throws DataNotFoundException
     * @throws LogicException
     * @throws ModelNotFoundException
     */
    public function read($params,  $with = [], $eachCall = null)
    {
        $id = $params['id'];
        $model = OrderCourseModel::field(OrderCourseModel::$column)->with($with)->findOrFail($id);
        $currentAt = Carbon::now()->timestamp;
        if (0 != $model['expired_at'] && $model['expired_at'] <= $currentAt) {
            throw new LogicException('该课程已过期');
        }

        if (is_callable($eachCall)) {
            $model = call_user_func($eachCall, $model);
        }
        return $model;
    }

    /**
     * 生成订单 debug
     *
     * @param $params
     * @return mixed
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function create($params)
    {
        $courseModel = CourseModel::findOrFail($params['id']);
        $amount = $courseModel['amount']; // 总金额
        // 获取可用元心币
        $availableCoin = invoke(StatisticsService::class)->getCoinAvailableCountByUserId($params['user_id']);
        $deductCoin = 1 == $params['is_coin'] ? $availableCoin : '0'; // 抵扣用元心币数量
        list($paymentAmount, $deductAmount, $deductCoin) = StringFacade::orderPayment($amount, $deductCoin);

        $orderInsert = [
            'user_id' => $params['user_id'],
            'course_id' => $courseModel['id'],
            'course_name' => $courseModel['name'],
            'course_image' => $courseModel['image'],
            'course_expired_days' => $courseModel['expired_days'],
            'course_content' => $courseModel['content'],
            'course_type' => $courseModel['type'],
            'course_chapter_section' => invoke(CourseService::class)->buildChapterSectionVideo($courseModel['id']),
            'total_amount' => $amount,
            'payment_amount' => $paymentAmount,
            'deduct_amount' => $deductAmount,
            'deduct_coin' => $deductCoin,
            'payment_type' => $params['payment_type'],
            'type' => $paymentAmount > 0 ? 1 : 3,
        ];

        Db::connect('communal')->transaction(function () use ($orderInsert, &$model) {
            $model = OrderCourseModel::create($orderInsert);
            $number = StringFacade::createStringById($model['id'], 'OS', 16);
            $model->save(['number' => $number]);

            // 支付成功前需要锁定元心币
            if ($orderInsert['deduct_coin'] > 0) {
                // 需要锁定
                invoke(StatisticsService::class)->freezeCoin($model['user_id'], $model['deduct_coin']);
            }
        });
        // return $model->bindAttr('hasSub'); // 框架无效
        // return OrderMultiModel::with(['hasSub'])->find($model['id']);
        return $model;
    }

    /**

    /**
     * 支付订单
     *
     * @param $order
     * @return string
     * @throws LogicException
     */
    public function payment($order)
    {
        $result = '';
        if (empty($order['number_md5'])) {
            // 订单首次支付
            // 支付订单md5编号
            $numberMd5 = md5($order['number'] . Carbon::now());
            $order->save(['number_md5' => $numberMd5]);
        } else {
            $numberMd5 = $order['number_md5'];
        }
        if ($order['payment_amount'] > 0 && 1 == $order['type']) {
            switch ($order['payment_type']) {
                case 1:
                    // 微信支付
                    $params = [
                        'subject' => $order['course_name'],
                        'outTradeNo' => $numberMd5,
                        'totalAmount' => (int)bcmul($order['payment_amount'], '100'),
                        'attach' => urlencode(json_encode([
                            // 2 对应 50300_order_course(单个订单支付)
                            'order_type' => 3,
                        ], JSON_UNESCAPED_UNICODE)),
                        'notify_url' => config('domain.api') . '/callback/wechatpay'
                    ];
                    $result = WechatpayFacade::v3PayTransactionsApp($params);
                    $result = WechatpayFacade::v3AppSign($result['prepay_id']);
                    break;
                case 2:
                    // 支付宝支付
                    $params = [
                        'subject' => $order['course_name'],
                        'outTradeNo' => $numberMd5,
                        'totalAmount' => $order['payment_amount'],
                        'optionalArgs' => [
                            'passback_params' => urlencode(json_encode([
                                // 3 对应 50300_order_course
                                'order_type' => 3,
                            ], JSON_UNESCAPED_UNICODE)),
                            'query_options' => [],
                        ],
                        'notify_url' => config('domain.api') . '/callback/alipay'
                    ];
                    $result = ALiPayFacade::paymentAppPay($params);
                    break;
                default:
                    break;
            }
        }

        return $result;
    }

    /**
     * 支付订单 debug
     *
     * @param $order
     * @return string
     * @throws LogicException
     */
    public function paymentPage($order)
    {
        $result = '';
        if (empty($order['number_md5'])) {
            // 订单首次支付
            // 支付订单md5编号
            $numberMd5 = md5($order['number'] . Carbon::now());
            $order->save(['number_md5' => $numberMd5]);
        } else {
            $numberMd5 = $order['number_md5'];
        }
        if ($order['payment_amount'] > 0 && 1 == $order['type']) {
            switch ($order['payment_type']) {
                case 1:
                    // 微信支付
                    throw new LogicException('暂不支持微信支付');
                    break;
                case 2:
                    // 支付宝支付
                    $params = [
                        'subject' => $order['course_name'],
                        'outTradeNo' => $numberMd5,
                        'totalAmount' => $order['payment_amount'],
                        'optionalArgs' => [
                            'passback_params' => urlencode(json_encode([
                                // 3 对应 50300_order_course
                                'order_type' => 3,
                            ], JSON_UNESCAPED_UNICODE)),
                            'query_options' => [],
                        ],
                        'notify_url' => config('domain.api') . '/callback/alipay'
                    ];
                    $result = ALiPayFacade::paymentPagePay($params);
                    break;
                default:
                    break;
            }
        }

        return $result;
    }

    /**
     * 支付宝回调处理
     *
     * @param $params
     */
    public function aLiPayCallback($params)
    {
        Log::debug('Callback_ALiPay_order_course_params: ' . var_export($params, true));
        // 签名验证后
        $check = ALiPayFacade::paymentCommonVerifyNotify($params);
        Log::debug('Callback_ALiPay_order_course_out_trade_no: ' . $params['out_trade_no'] . ',验签结果: ' . $check);
        if (!$check) {
            return ;
        }

        $update = [];
        $tradeStatus = $params['trade_status'] ?? '';
        if (isset($params['trade_status']) && 'TRADE_SUCCESS' == $params['trade_status']) {
            // 支付成功
            event('OrderCoursePaySuccess', ['out_trade_no' => $params['out_trade_no'], 'trade_status' => $tradeStatus]); // 可队列
        } else {
            // 更新订单信息
            $update['trade_status'] = $tradeStatus;
            OrderCourseModel::where('number_md5', $params['out_trade_no'])->update($update);
        }
    }

    /**
     * 微信回调处理
     *
     * @param $params
     */
    public function wechatPayCallback($params)
    {
        Log::debug('Callback_wechatPay_order_course_params: ' . var_export($params, true));

        $update = [];
        $tradeStatus = $params['trade_state'] ?? '';
        if (isset($params['trade_state']) && 'SUCCESS' == $params['trade_state']) {
            // 支付成功
            event('OrderCoursePaySuccess', ['out_trade_no' => $params['out_trade_no'], 'trade_status' => $tradeStatus]); // 可队列
        } else {
            // 更新订单信息
            $update['trade_status'] = $tradeStatus;
            OrderCourseModel::where('number_md5', $params['out_trade_no'])->update($update);
        }
    }

    /**
     * 支付成功
     *
     * @param $outTradeNo
     * @param string $tradeStatus
     */
    public function paySuccess($outTradeNo, $tradeStatus = '')
    {
        $model = OrderCourseModel::where('number_md5', $outTradeNo)->findOrEmpty();
        if (!$model->isEmpty() && 1 == $model['type']) {
            // 去事件里面搞吧...
            Db::connect('communal')->transaction(function () use ($model, $outTradeNo, $tradeStatus) {
                // 支付成功后状态变更
                // 支付成功后设置过期时间
                // 获取过期时间
                $expiredAt = $model['course_expired_days'] > 0 ? Carbon::now()->addDays((int)$model['course_expired_days'])->timestamp : 0;
                $model->save(['type' => 3, 'trade_status' => $tradeStatus, 'expired_at' => $expiredAt]);
                // 支付成功后抵扣用的元心币需要扣除
                // 增加元心币消费明细
                invoke(StatisticsService::class)->consumeFreezeCoin($model['user_id'], $model['deduct_coin'], 2);
                // 增加累积消费 并 试图返点
                invoke(StatisticsService::class)->accrueCost($model['user_id'], $model['payment_amount']);
            });
        }
    }
}
